from urllib.parse import urlencode, parse_qs
from urllib.request import urlopen
import json
from django.conf import settings

from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, BadData
from . import constants

import logging

# 日志记录器
from .exceptions import QQAPIException
logger = logging.getLogger('django')


class OAuthQQ(object):
    """QQ 认证辅助工具类"""

    def __init__(self, client_id=None, client_secret=None, redirect_uri=None, state=None):
        """构造方法, 用于接受所有的工具方法需要使用到的参数"""
        self.client_id = client_id or settings.QQ_CLIENT_ID
        self.client_secret = client_secret or settings.QQ_CLIENT_SECRET
        self.redirect_uri = redirect_uri or settings.QQ_REDIRECT_URI
        self.state = state or settings.QQ_STATE

    def get_qq_login_url(self):
        """
        获取qq登录的网址

        QQ登录参数:
        QQ_CLIENT_ID = '101474184'
        QQ_CLIENT_SECRET = 'c6ce949e04e12ecc909ae6a8b09b637c'
        QQ_REDIRECT_URI = 'http://www.meiduo.site:8080/oauth_callback.html'
        QQ_STATE = '/'

        :return: url
        """
        # 1, 准备url:  必须自己补充?
        login_url = 'https://graph.qq.com/oauth2.0/authorize?'
        # 2, 准备参数
        # 'https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=xx..
        params = {
            'response_type': 'code',  # 表示扫码后未来得到的code(获取access_token的凭据)
            'client_id': self.client_id,  # 创建应用时 的标识appid
            'redirect_uri': self.redirect_uri,  # 扫码成功后回调地址
            'state': self.state,  # next参数, client端的状态值。用于第三方应用防止CSRF攻击
            'scope': 'get_user_info',
        }
        login_url += urlencode(params)
        return login_url

    def get_access_token(self, code):
        """
        获取access_token

        :param code:qq提供的code

        :return:access_token
        """

        # 准备url  PC网站：https://graph.qq.com/oauth2.0/token
        get_token_url = 'https://graph.qq.com/oauth2.0/token?'

        # 准备参数
        params = {
            'grant_type': "authorization_code",
            'client_id': self.client_id,
            'client_secret': self.client_secret,
            'code': code,
            'redirect_uri': self.redirect_uri
        }


        try:
            get_token_url += urlencode(params)
            # 使用code向QQ服务器发送请求 读取到的数据为bytes类型
            response_data = urlopen(get_token_url).read()

            # 转码成字符串类型
            response_str = response_data.decode()

            # 将查询字符串格式数据转换为python的字典
            response_dict = parse_qs(response_str)

            # 从字典中取出access_token
            access_token = response_dict.get('access_token')[0]
        except Exception as e:
            logger.error(e)

            # 抛出异常
            raise QQAPIException('获取access_token失败')

        return access_token

    def get_openid(self, access_token):
        """
        获取用户的openid
        :param access_token: qq提供的access_token
        :return: open_id
        """
        # 准备url
        get_openid_url = 'https://graph.qq.com/oauth2.0/me?access_token=' + access_token

        # 使用access_token向QQ服务器发送请求, 读取到的是bytes类型, 将bytes类型的数据转码为字符串
        response_str = urlopen(get_openid_url).read().decode()
        try:
            # callback({"client_id": "YOUR_APPID", "openid": "YOUR_OPENID"});
            response_dict = json.loads(response_str[10:-4])
        except Exception:
            data = parse_qs(response_str)
            logger.error('code=%s msg=%s' % (data.get('code'), data.get('msg')))
            raise QQAPIException
            # 从字典中取出openid
        openid = response_dict.get('openid', None)
        return openid

    @staticmethod
    def generate_save_user_token(openid):
        """
        生成保存用户数据的token
        :param openid: 用户的openid
        :return: token
        """
        serializer = Serializer(settings.SECRET_KEY, expires_in=constants.SAVE_QQ_USER_TOKEN_EXPORES)
        data = {"openid": openid}
        token = serializer.dumps(data)
        return token.decode()

    @staticmethod
    def check_save_user_token(token):
        """
        检验保存用户数据的token
        :param token: token
        :return: openid or None
        """
        serializer = Serializer(settings.SECRET_KEY, expires_in=constants.SAVE_QQ_USER_TOKEN_EXPORES)
        try:
            data = serializer.loads(token)
        except BadData:
            return None
        else:
            return data.get('openid')